package com.stt.experiment.websocket.action.dispatcher;

import com.stt.experiment.websocket.action.dispatcher.annotation.Interceptor;
import com.stt.experiment.websocket.action.dispatcher.annotation.ReqHandlers;
import com.stt.experiment.websocket.action.dispatcher.annotation.ReqNames;
import com.stt.experiment.websocket.action.dispatcher.exception.MethodNotFoundException;
import com.stt.experiment.websocket.action.dispatcher.intercepter.ActionInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.lang.reflect.InvocationTargetException;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * 请求调度分发
 */
@Component
public class ActionDispatcher {

    private final ThreadPoolExecutor workers;  // 多线程 - 线程池
    private Map<ReqNames, ReqHandlers> handlersMap;
    private List<ActionInterceptor> interceptors = new LinkedList<>();

    @Autowired
    public ActionDispatcher(ActionConfig actionConfig) {
        this.workers = new ThreadPoolExecutor(actionConfig.getCorePoolSize(), actionConfig.getMaximumPoolSize(), (long)actionConfig.getKeepAliveTime(), actionConfig.getTimeUnit(), new LinkedBlockingQueue());
    }

    public Future<Object> doDispatcher(String module, String cmd, Object params) {
        return this.workers.submit(() -> {
            return this.invoke(module, cmd, params);
        });
    }

    private Object invoke(String module, String cmd, Object params) throws InvocationTargetException, IllegalAccessException {
        if (this.handlersMap == null) {
            throw new RuntimeException("handlerMap is not ready yet");
        } else {
            ReqNames reqNames = new ReqNames(module, cmd);
            ReqHandlers reqHandlers = this.handlersMap.get(reqNames);
            if (reqHandlers == null) {
                reqNames = new ReqNames(module, "");
                reqHandlers = this.handlersMap.get(reqNames);
                if (reqHandlers == null) {
                    reqNames = new ReqNames("", "");
                    reqHandlers = this.handlersMap.get(reqNames);
                }
            }

            if (reqHandlers != null) {
                return this.doInteceptor(reqNames, params)?reqHandlers.getMethod().invoke(reqHandlers.getTarget(), new Object[]{params}):null;
            } else {
                throw new MethodNotFoundException("请求不存在");
            }
        }
    }

    private boolean doInteceptor(ReqNames reqNames, Object buffer) {
        Iterator var3 = this.interceptors.iterator();
        ActionInterceptor interceptor;
        do {
            if (!var3.hasNext()) {
                return true;
            }
            interceptor = (ActionInterceptor)var3.next();
        } while (!this.needIntercept(interceptor, reqNames) || interceptor.preHandle(buffer));
        return false;
    }

    private boolean needIntercept(ActionInterceptor interceptor, ReqNames reqNames) {
        Interceptor interceptorAnno = interceptor.getClass().getAnnotation(Interceptor.class);
        return this.include(interceptorAnno.includes(), reqNames) && !this.exclude(interceptorAnno.excludes(), reqNames);
    }

    private boolean include(String[] includes, ReqNames reqNames) {
        if (includes.length > 0) {
            String[] var3 = includes;
            int var4 = includes.length;
            for (int var5 = 0; var5 < var4; ++var5) {
                String include = var3[var5];
                if (reqNames.toString().equals(include)) {
                    return true;
                }
            }
        }
        return true;
    }

    private boolean exclude(String[] excludes, ReqNames reqNames) {
        if (excludes.length > 0) {
            String[] var3 = excludes;
            int var4 = excludes.length;

            for (int var5 = 0; var5 < var4; ++var5) {
                String exclude = var3[var5];
                if (reqNames.toString().equals(exclude)) {
                    return true;
                }
            }
        }
        return false;
    }

    public void registerInterceptor(ActionInterceptor interceptor) {
        this.interceptors.add(interceptor);
    }

    public void setHandlerMap(Map<ReqNames, ReqHandlers> handlerMap) {
        this.handlersMap = handlerMap;
    }

    public void setInterceptors(List<ActionInterceptor> interceptors) {
        this.interceptors = interceptors;
    }
}
